#include "StdAfx.h"
#include "VectorDataProvider.h"
#include "ogr_srs_api.h"
#include "VectorLayer.h"
#include "PointGeometry.h"

CVectorDataProvider::CVectorDataProvider(TCHAR* pFilePath):m_pFilePath(pFilePath)
{	
	m_geometries = new CArray<CGeometry*>();
}

CVectorDataProvider::~CVectorDataProvider(void)
{
	//if (m_pFilePath)
	//{
	//	delete [] m_pFilePath;
	//}
	//if (m_Geometrys)
	//{
	//	for (int ii = 0; ii < m_Geometrys->GetCount(); ii++)
	//	{
	//		delete m_Geometrys->GetAt(ii);
	//	}
	//	m_Geometrys->RemoveAll();
	//	delete m_Geometrys;
	//}
}

CVectorLayer* CVectorDataProvider::createVectorLayer()
{
	if ( 0 >= OGRGetDriverCount() )
	{
		OGRRegisterAll();
	}
	m_pOgrDataSource = OGROpen((LPSTR)(LPCTSTR)m_pFilePath,TRUE, &m_pOgrDriver);

	if (m_pOgrDataSource)
	{
		m_pOgrLayer = OGR_DS_GetLayer(m_pOgrDataSource,0);
	}

	OGREnvelope ogrEnvelope;
	OGR_L_GetExtent(m_pOgrLayer, &ogrEnvelope, TRUE);
	m_layerEnvelope.m_maxX = ogrEnvelope.MaxX;
	m_layerEnvelope.m_minX = ogrEnvelope.MinX;
	m_layerEnvelope.m_maxY = ogrEnvelope.MaxY;
	m_layerEnvelope.m_minY = ogrEnvelope.MinY;
	OGRFeatureDefnH featureDef = OGR_L_GetLayerDefn(m_pOgrLayer);
	if (featureDef)
	{
		m_geomType = OGR_FD_GetGeomType(featureDef);
	}
	m_geometryCount = OGR_L_GetFeatureCount(m_pOgrLayer,TRUE);

	//create coordinate system
	if (m_pOgrDriver)
	{
		const TCHAR* driverName = OGR_Dr_GetName(m_pOgrDriver);
		if (0 == _tcscmp((LPCSTR)driverName,"ESRI Shapefile"))
		{
			/*CString layerPath(m_pFilePath);
			int index = layerPath.ReverseFind('.');
			CString layerPathWithoutExt = layerPath.Left(index);
			CString layerProjFile = layerPathWithoutExt;
			layerProjFile += ".qpj";
			CFile projFile;*/			
			const TCHAR* fileExt = _tcsrchr(m_pFilePath,_T('.'));
			int index = _tcslen(m_pFilePath) - _tcslen(fileExt);
			TCHAR* layerProjFile = new TCHAR[index+5];
			memcpy(layerProjFile,m_pFilePath,(index)*sizeof(TCHAR));
			//_tcsncpy(driverName,layerProjFile,index);
			memcpy(layerProjFile+index,_T(".qpj"),5*sizeof(TCHAR));
			CFile projFile;
			if (projFile.Open(layerProjFile,CFile::modeRead))
			{
				TCHAR* wktString;
				projFile.Read(wktString, projFile.GetLength());
				projFile.Close();
				createCoordinateFromWkt(wktString);
				delete [] layerProjFile;
				return NULL;
			}
			delete [] layerProjFile;
		}
	}

	m_crs = OGR_L_GetSpatialRef(m_pOgrLayer);
	if (m_crs == NULL)
	{
		return NULL;
	}

	m_unitType = UnitType::UnknownUnit;
	// Of interest to us is that this call adds in a unit parameter if
	// one doesn't already exist.
	OSRFixup(m_crs);

	if (OSRIsProjected(m_crs))
	{
                
	}
	else
	{
		TCHAR* unitName;
		OSRGetAngularUnits(m_crs, &unitName);
		if (0 == _tcscmp(unitName, _T("Degree")))
		{
			m_unitType = UnitType::Degrees;
		}
	}

	createGeometrys();

    // Find map name from file path
    TCHAR* pFileNameWithoutExt;
    {
        const TCHAR* fileNameWithExt = _tcsrchr(m_pFilePath,_T('\\'));
        const TCHAR* fileExt = _tcsrchr(m_pFilePath,_T('.'));
        int len = _tcslen(fileNameWithExt) - _tcslen(fileExt);
        pFileNameWithoutExt = new TCHAR[len];
        ZeroMemory(pFileNameWithoutExt,len);
        memcpy(pFileNameWithoutExt,fileNameWithExt+sizeof(TCHAR),(len-1)*sizeof(TCHAR));
    }

	CVectorLayer* layer = new CVectorLayer();
	layer->m_geomType = m_geomType;
	layer->m_unitType = m_unitType;
	layer->m_geometryCount = m_geometryCount;
	layer->m_crs = m_crs;
	layer->m_layerEnvelope = m_layerEnvelope;
	layer->m_geometries = m_geometries;
    layer->m_layerName = pFileNameWithoutExt;
	m_geometries = NULL;
	return layer;
}

bool CVectorDataProvider::createCoordinateFromWkt(TCHAR* theWkt)
{
	if (theWkt == NULL || 1 == strlen(theWkt))
	{
		return false;
	}

	OGRErr myInputResult = OSRImportFromWkt( m_crs, &theWkt);
	return myInputResult;
}

bool CVectorDataProvider::createGeometrys()
{
	OGR_L_ResetReading(m_pOgrLayer);
	OGRFeatureH feature;
	double *x;
	double *y;
	int *nPoints;
	int *numRings;
	int *numPolygons;
	int numLineStrings;
	int idx, jdx, kdx;
	unsigned char *ptr;
	while ((feature = OGR_L_GetNextFeature(m_pOgrLayer)) != NULL)
	{
		OGRGeometryH ogrGeom = OGR_F_GetGeometryRef(feature);
		if (NULL == ogrGeom)
		{
			continue;;
		}

		OGRwkbGeometryType geomType = OGR_G_GetGeometryType(ogrGeom);
		
		bool hasZValue = false;
		_TUCHAR* wkbStr = new _TUCHAR[OGR_G_WkbSize(ogrGeom)];
		OGR_G_ExportToWkb(ogrGeom, (OGRwkbByteOrder)(( htonl( 1 ) == 1 ) ? 0 : 1),wkbStr);
		switch ( geomType )
		{
			case OGRwkbGeometryType::wkbPoint25D:
				hasZValue = true;
			case OGRwkbGeometryType::wkbPoint:
				{
					double x = *(( double * )( wkbStr + 5 ) );
					double y = *(( double * )( wkbStr + 5 + sizeof( double ) ) );
					CPointGeometry* pointF = new CPointGeometry(x,y);
					m_geometries->Add(pointF);
				}
				break;
			case OGRwkbGeometryType::wkbLineString25D:
				hasZValue = true;
			case OGRwkbGeometryType::wkbLineString:
				{
					ptr = wkbStr + 5;
					nPoints = ( int * ) ptr;
					ptr = wkbStr + 1 + 2 * sizeof( int );
					CLineGeometry* pLine = new CLineGeometry();
					for ( idx = 0; idx < *nPoints; idx++ )
					{
						x = ( double * ) ptr;
						ptr += sizeof( double );
						y = ( double * ) ptr;
						ptr += sizeof( double );
						if ( hasZValue )
						{
							ptr += sizeof( double );
						}
						pLine->addPoint(new CPointGeometry(*x,*y));
					}
					m_geometries->Add(pLine);
				}
				break;
			case OGRwkbGeometryType::wkbMultiLineString25D:
				hasZValue = true;
			case OGRwkbGeometryType::wkbMultiLineString:
				{
					numLineStrings = ( int )( wkbStr[5] );
					ptr = ( wkbStr + 9 );
					CMultiLineGeometry* pMultiLine = new CMultiLineGeometry();
					for ( jdx = 0; jdx < numLineStrings; jdx++ )
					{
						//QgsPolyline sequence;

						// each of these is a wbklinestring so must handle as such
						//lsb = *ptr;
						ptr += 5;   // skip type since we know its 2
						nPoints = ( int * ) ptr;
						ptr += sizeof( int );
						CLineGeometry* pLine = new CLineGeometry();
						for ( idx = 0; idx < *nPoints; idx++ )
						{
							x = ( double * ) ptr;
							ptr += sizeof( double );
							y = ( double * ) ptr;
							ptr += sizeof( double );
							if ( hasZValue )
							{
								ptr += sizeof( double );
							}
							pLine->addPoint(new CPointGeometry(*x,*y));
						}	
						pMultiLine->addLine(pLine);
					}
					m_geometries->Add(pMultiLine);
					break;
				}
			case OGRwkbGeometryType::wkbPolygon25D:
				hasZValue = true;
			case OGRwkbGeometryType::wkbPolygon:
				{
					// get number of rings in the polygon
					numRings = ( int * )( wkbStr + 1 + sizeof( int ) );
					ptr = wkbStr + 1 + 2 * sizeof( int );
					CPolygonGeometry* pPolygon = new CPolygonGeometry();
					for ( idx = 0; idx < *numRings; idx++ )
					{
						// get number of points in the ring
						nPoints = ( int * ) ptr;
						ptr += 4;
						CLineRingGeometry* pLineRing = new CLineRingGeometry();
						for ( jdx = 0; jdx < *nPoints; jdx++ )
						{
							// add points to a point array for drawing the polygon
							x = ( double * ) ptr;
							ptr += sizeof( double );
							y = ( double * ) ptr;
							ptr += sizeof( double );
							if ( hasZValue )
							{
								ptr += sizeof( double );
							}
							pLineRing->addPoint(new CPointGeometry(*x,*y));
						}
						pPolygon->addLineRing(pLineRing);
					}
					m_geometries->Add(pPolygon);
					break;
				}
			case OGRwkbGeometryType::wkbMultiPolygon25D:
				hasZValue = true;
			case OGRwkbGeometryType::wkbMultiPolygon:
				{
					ptr = wkbStr + 5;
					numPolygons = ( int * ) ptr;
					ptr = wkbStr + 9;
					CMultiPolygonGeometry* pMultiPolygon = new CMultiPolygonGeometry();
					for ( kdx = 0; kdx < *numPolygons; kdx++ )
					{
						//skip the endian and mGeometry type info and
						// get number of rings in the polygon
						ptr += 5;
						numRings = ( int * ) ptr;
						ptr += 4;
						CPolygonGeometry* pPolygon = new CPolygonGeometry();
						for ( idx = 0; idx < *numRings; idx++ )
						{
		     				// get number of points in the ring
							nPoints = ( int * ) ptr;
							ptr += 4;
							CLineRingGeometry* pLineRing = new CLineRingGeometry();
							for ( jdx = 0; jdx < *nPoints; jdx++ )
							{
								// add points to a point array for drawing the polygon
								x = ( double * ) ptr;
								ptr += sizeof( double );
								y = ( double * ) ptr;
								ptr += sizeof( double );
								if ( hasZValue )
								{
									ptr += sizeof( double );
								}
								pLineRing->addPoint(new CPointGeometry(*x,*y));
							}
							pPolygon->addLineRing(pLineRing);
						}
						pMultiPolygon->addPolygon(pPolygon);
					}
					m_geometries->Add(pMultiPolygon);
					break;	
				}			
			default:
				break;
		}
		delete [] wkbStr;
		OGR_F_Destroy(feature);
	}
	return true;
}


